{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": [],
      "collapsed_sections": [
        "2MAeSKgzbi7S"
      ]
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "cells": [
    {
      "cell_type": "code",
      "source": [
        "from google.colab import drive\n",
        "drive.mount('/content/drive')"
      ],
      "metadata": {
        "id": "JMgRmm1nrQRG",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "aafe65cb-d201-4371-a7f1-85d1455bef1c"
      },
      "execution_count": 136,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Drive already mounted at /content/drive; to attempt to forcibly remount, call drive.mount(\"/content/drive\", force_remount=True).\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "\n",
        "# Load data"
      ],
      "metadata": {
        "id": "F3QakannbL6k"
      }
    },
    {
      "cell_type": "code",
      "execution_count": 137,
      "metadata": {
        "id": "KNIsQTdcpSjk",
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "outputId": "613c8727-8554-4cfe-d5d4-e9b1bbeea207"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "(820, 600)\n",
            "(820,)\n"
          ]
        }
      ],
      "source": [
        "\n",
        "import pickle\n",
        "from sklearn.model_selection import train_test_split\n",
        "from sklearn.svm import SVC ,LinearSVC\n",
        "from sklearn.metrics import accuracy_score\n",
        "\n",
        "# Load features from the pickle file\n",
        "with open('/content/drive/MyDrive/ECG_Project/segments.pkl', 'rb') as file:\n",
        "    X = pickle.load(file)\n",
        "\n",
        "# Load labels from the pickle file\n",
        "with open('/content/drive/MyDrive/ECG_Project/Labels.pkl', 'rb') as file:\n",
        "    y = pickle.load(file)\n",
        "\n",
        "print(X.shape)\n",
        "print(y.shape)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Preprocessing helper functions\n",
        "  this section contains all functions needed for preprocessing and feature extraction\n"
      ],
      "metadata": {
        "id": "2MAeSKgzbi7S"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "import math\n",
        "import numpy as np\n",
        "import pandas as pd\n",
        "from scipy.signal import butter, filtfilt, find_peaks\n",
        "from sklearn.preprocessing import StandardScaler\n",
        "\n",
        "\n",
        "# at first from data visualization we find that channel i and vx is the most real signals ,so we will\n",
        "# use these two channels and drop others\n",
        "\n",
        "\n",
        "# Function to apply Butterworth low-pass filter to ECG signal\n",
        "def butter_lowpass_filter(data, cutoff_, fs_, order_):\n",
        "    nyquist = 0.5 * fs_\n",
        "    normal_cutoff = cutoff_ / nyquist\n",
        "    b, a = butter(order_, normal_cutoff, btype='low', analog=False)\n",
        "    y = filtfilt(b, a, data)\n",
        "    return y\n",
        "\n",
        "\n",
        "def butter_bandpass_filter(data, low_cut, high_cut, fs_, order_):\n",
        "    nyquist = 0.5 * fs_\n",
        "    low = low_cut / nyquist\n",
        "    high = high_cut / nyquist\n",
        "    b, a = butter(order_, [low, high], btype='band', analog=False)\n",
        "    y = filtfilt(b, a, data)\n",
        "    return y\n",
        "\n",
        "\n",
        "def Normalize(df):\n",
        "    # Initialize StandardScaler\n",
        "    scaler = StandardScaler()\n",
        "    # Fit and transform the data\n",
        "    normalized_data = scaler.fit_transform(df)\n",
        "\n",
        "    # Convert the normalized data back to a DataFrame\n",
        "    df_normalized = pd.DataFrame(normalized_data, columns=df.columns)\n",
        "\n",
        "    # Replace the original DataFrame with the normalized one\n",
        "    df[df.columns] = df_normalized\n",
        "\n",
        "    return df\n",
        "\n",
        "\n",
        "def my_findpeaks(column, threshold_low=None, threshold_high=None):\n",
        "    result = []\n",
        "    m = len(column)\n",
        "    for indexer in range(4, m - 4):\n",
        "        if threshold_low > column[indexer] or column[indexer] > threshold_high:\n",
        "            if (column[indexer - 1] < column[indexer] > column[indexer + 1]) or (\n",
        "                    column[indexer - 1] > column[indexer] < column[indexer + 1]):\n",
        "                result.append(indexer)\n",
        "    return result\n",
        "\n",
        "\n",
        "def moving_average(signal_, window_size):\n",
        "    \"\"\"\n",
        "    Calculates the moving average of a signal using convolution.\n",
        "\n",
        "    Parameters:\n",
        "    - signal (numpy array): Input signal for which the moving average is calculated.\n",
        "    - window_size (int): Size of the moving average window.\n",
        "\n",
        "    Returns:\n",
        "    - ma_signal (numpy array): Signal after applying the moving average.\n",
        "    \"\"\"\n",
        "    weights = np.ones(window_size) / window_size  # Create a weight array for the moving average\n",
        "    ma_signal = np.convolve(signal_, weights, mode='same')  # Perform convolution to calculate moving average\n",
        "\n",
        "    return ma_signal  # Return the signal after applying the moving average\n",
        "\n",
        "\n",
        "def pan_and_tompkins(channel_record, sampling_rate):\n",
        "    \"\"\"\n",
        "    Detects R-peaks in an ECG signal using the Pan-Tompkins algorithm.\n",
        "\n",
        "    Parameters:\n",
        "\n",
        "    - ecg_signal (numpy array): ECG signal to detect R-peaks from.\n",
        "    - sampling_rate (float): Sampling rate of the ECG signal in Hz.\n",
        "\n",
        "    Returns:\n",
        "    - r_peaks (list): List of indices corresponding to detected R-peaks.\n",
        "    \"\"\"\n",
        "    # Constants for Pan-Tompkins algorithm\n",
        "    window_size = int(0.1 * sampling_rate)  # Window size for moving average integration\n",
        "    refractory_period = int(0.2 * sampling_rate)  # Refractory period for R-peaks\n",
        "    threshold_factor = 0.6  # Factor for setting R-peaks detection threshold\n",
        "\n",
        "    # Preprocessing: Bandpass filter, differentiation, and squaring\n",
        "    filtered_signal = channel_record  # Apply Bandpass filter\n",
        "    differentiated_signal = np.gradient(filtered_signal)  # Differentiation\n",
        "    squared_signal = differentiated_signal ** 2  # Squaring\n",
        "\n",
        "    # Moving average integration\n",
        "    ma_signal = moving_average(squared_signal, window_size)\n",
        "\n",
        "    # Find the maximum value as the threshold for R-peaks\n",
        "    threshold = threshold_factor * np.max(ma_signal)\n",
        "\n",
        "    # Initialize variables\n",
        "    r_peaks = []  # List to store R-peak indices\n",
        "    last_r_peak = 0  # Variable to track last detected R-peak index\n",
        "\n",
        "    # Detection of R-peaks\n",
        "    for i in range(window_size, len(ma_signal) - refractory_period):\n",
        "        if ma_signal[i] > threshold and i - last_r_peak > refractory_period:\n",
        "            # Check if it is the maximum in the local window\n",
        "            if ma_signal[i] >= np.max(ma_signal[i - window_size: i + window_size + 1]):\n",
        "                r_peaks.append(i)\n",
        "                last_r_peak = i\n",
        "\n",
        "    return r_peaks  # Return the list of detected R-peaks\n",
        "\n",
        "\n",
        "def Segmentation(channel_record):\n",
        "    \"\"\"\n",
        "    Segments an ECG signal around R-peaks.\n",
        "\n",
        "    Parameters:\n",
        "    - signal (numpy array): ECG signal to be segmented.\n",
        "\n",
        "    Returns:\n",
        "    - segmented_signal (numpy array): Segmented signal around R-peaks.\n",
        "    - beforeR (int): Length of the segment before R-peak.\n",
        "    - afterR (int): Length of the segment after R-peak.\n",
        "    \"\"\"\n",
        "    peaks = pan_and_tompkins(channel_record, 1000)  # Detect R-peaks in the signal\n",
        "    # Calculate average heartbeat length based on the first three R-peaks\n",
        "    heartbeatLength = ((peaks[1] - peaks[0]) + (peaks[2] - peaks[1])) / 2\n",
        "    beforeR = int((1 / 3) * heartbeatLength)  # Length of segment before R-peak\n",
        "    afterR = int((2 / 3) * heartbeatLength)  # Length of segment after R-peak\n",
        "    segmented_signal = []\n",
        "    for j in range(len(peaks) - 1):\n",
        "        # Segment the signal around the third R-peak\n",
        "        temp = channel_record[peaks[j] - beforeR: peaks[j] + afterR]\n",
        "        segmented_signal.append(temp[0:600])\n",
        "\n",
        "    return np.array(segmented_signal)  # Return the segmented signal\n",
        "\n",
        "\n",
        "def preprocessing(data, cutoff_low_, cutoff_high, fs_, order_):\n",
        "    new_data = {}\n",
        "    segments_dict = {}\n",
        "    for participant_, data_ in data.items():\n",
        "        segment = object\n",
        "        # Apply filter to each column (ECG signal) except the last one\n",
        "        preprocessed_data = data_.copy()\n",
        "        iteration = 0\n",
        "        for column in preprocessed_data.columns[:]:\n",
        "            preprocessed_data[column] = butter_bandpass_filter(preprocessed_data[column],\n",
        "                                                               cutoff_low_, cutoff_high, fs_, order_)\n",
        "            if iteration == 0:\n",
        "                segment = Segmentation(preprocessed_data[column])\n",
        "                iteration += 1\n",
        "            else:\n",
        "                segment = np.vstack((segment, Segmentation(preprocessed_data[column])))\n",
        "            preprocessed_data[column] = preprocessed_data[column] - np.mean(preprocessed_data[column])\n",
        "\n",
        "        segments_dict[participant_] = segment\n",
        "        new_data[participant_] = preprocessed_data\n",
        "\n",
        "    return new_data, segments_dict\n",
        "\n",
        "\n",
        "def prepare_segments_array(segments_):\n",
        "    Labels_ = []\n",
        "    segments_array_ = []\n",
        "    for patient in segments_:\n",
        "        cur_patient = segments_[patient]\n",
        "        for c in range(cur_patient.shape[0]):\n",
        "            g = np.array(cur_patient[c])\n",
        "            segments_array_.append(g)\n",
        "            Labels_.append(patient)\n",
        "    segments_array_ = np.array(segments_array_)\n",
        "    Labels_ = np.array(Labels_)\n",
        "    return segments_array_, Labels_\n",
        "\n",
        "\n",
        "def minimumRadiusOfCurvature(signal, peakIndex, windowSize, isOnset):\n",
        "    \"\"\"\n",
        "    Calculates the minimum radius of curvature in a signal segment around a peak index.\n",
        "\n",
        "    Parameters:\n",
        "    - signal (numpy array): Input signal.\n",
        "    - peakIndex (int): Index of the peak around which the radius of curvature is calculated.\n",
        "    - windowSize (int): Size of the window around the peak index.\n",
        "    - isOnset (bool): Flag indicating whether the peak is an onset or not.\n",
        "\n",
        "    Returns:\n",
        "    - minCurveIndex (int): Index corresponding to the minimum radius of curvature.\n",
        "    \"\"\"\n",
        "    t = np.arange(0, len(signal), 1)  # Time array\n",
        "    X = peakIndex  # Peak index around which the radius of curvature is calculated\n",
        "    if isOnset:\n",
        "        Y = peakIndex - windowSize  # Start index of the window\n",
        "        if Y < 0:\n",
        "            Y = 0  # Ensure Y is within signal range\n",
        "    else:\n",
        "        Y = peakIndex + windowSize  # End index of the window\n",
        "        if Y >= len(signal):\n",
        "            Y = len(signal) - 1  # Ensure Y is within signal range\n",
        "\n",
        "    a = [t[Y] - t[X], signal[Y] - signal[X]]  # Vector 'a' representing the window direction\n",
        "    normA = math.sqrt(a[0] ** 2 + a[1] ** 2)  # Norm of vector 'a'\n",
        "    C = X  # Initialize current index 'C' to the peak index\n",
        "    allSigma = []  # List to store all curvature values\n",
        "    allSigmaIndex = []  # List to store corresponding indices for curvature values\n",
        "\n",
        "    # Calculate curvature for each point in the window\n",
        "    while C != Y:\n",
        "        c = [t[C] - t[X], signal[C] - signal[X]]  # Vector 'c' from peak to current point\n",
        "        sigma = abs(np.cross(a, c)) / normA  # Curvature calculation\n",
        "        allSigma.append(sigma)  # Append curvature value to list\n",
        "        allSigmaIndex.append(C)  # Append corresponding index to list\n",
        "        if isOnset:\n",
        "            C -= 1  # Move to the previous point in the window\n",
        "        else:\n",
        "            C += 1  # Move to the next point in the window\n",
        "\n",
        "    if len(allSigma) == 0:\n",
        "        return Y  # Return the end index of the window if no curvature values are calculated\n",
        "    else:\n",
        "        finalSigmaIndex = np.argmax(allSigma)  # Find the index of maximum curvature\n",
        "        return allSigmaIndex[finalSigmaIndex]  # Return the index corresponding to minimum radius of curvature\n",
        "\n",
        "\n",
        "def findP(segment_, qrs_onset):\n",
        "    \"\"\"\n",
        "    Finds the P-wave index in a signal given the QRS onset index.\n",
        "\n",
        "    Parameters:\n",
        "    - signal (numpy array): Input signal.\n",
        "    - qrs_onset (int): Index of the QRS onset in the signal.\n",
        "\n",
        "    Returns:\n",
        "    - p_peak (int): Index of the P-wave peak in the signal.\n",
        "    \"\"\"\n",
        "    j = qrs_onset - 200  # Start searching 200 samples before QRS onset\n",
        "    if j < 0:\n",
        "        j = 0  # Ensure j is within signal range\n",
        "\n",
        "    max_val = -np.inf  # Initialize maximum value as negative infinity\n",
        "    index = 0  # Initialize index variable\n",
        "\n",
        "    # Search for the P-wave peak index\n",
        "    while j < qrs_onset:\n",
        "        if segment_[j] > max_val:\n",
        "            max_val = segment_[j]\n",
        "            index = j\n",
        "        j += 1\n",
        "\n",
        "    p_peak = index  # P-wave peak index is the index of the maximum value before QRS onset\n",
        "    return p_peak  # Return the P-wave peak index\n",
        "\n",
        "\n",
        "def findqrsOffset(segment_, R):\n",
        "    \"\"\"\n",
        "    Finds the QRS offset index in a signal given the R-peak index.\n",
        "\n",
        "    Parameters:\n",
        "    - signal (numpy array): Input signal.\n",
        "    - R (int): Index of the R-peak in the signal.\n",
        "\n",
        "    Returns:\n",
        "    - qrs_offset (int): Index of the QRS offset in the signal.\n",
        "    \"\"\"\n",
        "    j = R + 50  # Start searching 50 samples after R-peak\n",
        "    if j > len(segment_) - 1:\n",
        "        j = len(segment_) - 1  # Ensure j is within signal range\n",
        "\n",
        "    # Search for the QRS offset index\n",
        "    while j < R and segment_[j] > segment_[j + 1]:\n",
        "        j -= 1\n",
        "\n",
        "    qrs_offset = j  # QRS offset index is the index where signal[j] <= signal[j + 1]\n",
        "    return qrs_offset  # Return the QRS offset index\n",
        "\n",
        "\n",
        "def findqrsOnset(segment_, R):\n",
        "    \"\"\"\n",
        "    Finds the QRS onset index in a signal given the R-peak index.\n",
        "\n",
        "    Parameters:\n",
        "    - signal (numpy array): Input signal.\n",
        "    - R (int): Index of the R-peak in the signal.\n",
        "\n",
        "    Returns:\n",
        "    - qrs_onset (int): Index of the QRS onset in the signal.\n",
        "    \"\"\"\n",
        "    j = R - 50  # Start searching 50 samples before R-peak\n",
        "    if j < 0:\n",
        "        j = 0  # Ensure j is within signal range\n",
        "\n",
        "    # Search for the QRS onset index\n",
        "    while j > R and segment_[j] > segment_[j - 1]:\n",
        "        j += 1\n",
        "\n",
        "    qrs_onset = j  # QRS onset index is the index where signal[j] <= signal[j - 1]\n",
        "    return qrs_onset  # Return the QRS onset index\n",
        "\n",
        "\n",
        "def findS(segment_, R):\n",
        "    \"\"\"\n",
        "    Finds the S-wave index in a signal given the R-peak index.\n",
        "\n",
        "    Parameters:\n",
        "    - signal (numpy array): Input signal.\n",
        "    - R (int): Index of the R-peak in the signal.\n",
        "\n",
        "    Returns:\n",
        "    - S (int): Index of the S-wave in the signal.\n",
        "    \"\"\"\n",
        "    j = R + 100  # Start searching 100 samples after R-peak\n",
        "    if j > len(segment_) - 1:\n",
        "        j = len(segment_) - 1  # Ensure j is within signal range\n",
        "    min_val = float('inf')  # Initialize minimum value as positive infinity\n",
        "    index = 0  # Initialize index variable\n",
        "\n",
        "    # Search for the minimum value after the R-peak\n",
        "    while j > R:\n",
        "        if segment_[j] < min_val:\n",
        "            min_val = segment_[j]\n",
        "            index = j\n",
        "        j -= 1\n",
        "\n",
        "    S = index  # S-wave index is the index of the minimum value after the R-peak\n",
        "    return S  # Return the S-wave index\n",
        "\n",
        "\n",
        "def findQ(segment_, R):\n",
        "    \"\"\"\n",
        "    Finds the Q-wave index in a signal given the R-peak index.\n",
        "\n",
        "    Parameters:\n",
        "    - signal (numpy array): Input signal.\n",
        "    - R (int): Index of the R-peak in the signal.\n",
        "\n",
        "    Returns:\n",
        "    - Q (int): Index of the Q-wave in the signal.\n",
        "    \"\"\"\n",
        "    j = R - 100  # Start searching 100 samples before R-peak\n",
        "    if j < 0:\n",
        "        j = 0  # Ensure j is within signal range\n",
        "    min_val = float('inf')  # Initialize minimum value as positive infinity\n",
        "    index = 0  # Initialize index variable\n",
        "\n",
        "    # Search for the minimum value before the R-peak\n",
        "    while j < R:\n",
        "        if segment_[j] < min_val:\n",
        "            min_val = segment_[j]\n",
        "            index = j\n",
        "        j += 1\n",
        "\n",
        "    Q = index  # Q-wave index is the index of the minimum value before the R-peak\n",
        "    return Q  # Return the Q-wave index\n",
        "\n",
        "\n",
        "def findR(segment_):\n",
        "    \"\"\"\n",
        "    Finds the peak (R-peak) index in a signal.\n",
        "\n",
        "    Parameters:\n",
        "    - signal (numpy array): Input signal.\n",
        "\n",
        "    Returns:\n",
        "    - R (int): Index of the peak (R-peak) in the signal.\n",
        "    \"\"\"\n",
        "    max_val = -float('inf')  # Initialize maximum value as negative infinity\n",
        "    index = 0  # Initialize index variable\n",
        "\n",
        "    # Iterate through the signal to find the maximum value and its index\n",
        "    for j in range(len(segment_)):\n",
        "        if segment_[j] > max_val:\n",
        "            max_val = segment_[j]\n",
        "            index = j\n",
        "\n",
        "    R = index  # Peak (R-peak) index is the index of the maximum value in the signal\n",
        "    return R  # Return the R-peak index\n",
        "\n",
        "\n",
        "def findPon(signal, p_peak, qrs_onset, isOnset):\n",
        "    \"\"\"\n",
        "    Finds the P-wave onset or offset index in a signal given the P-wave peak index,\n",
        "    QRS onset index, and a flag indicating whether it's an onset or offset.\n",
        "\n",
        "    Parameters:\n",
        "    - signal (numpy array): Input signal.\n",
        "    - p_peak (int): Index of the P-wave peak in the signal.\n",
        "    - qrs_onset (int): Index of the QRS onset in the signal.\n",
        "    - isOnset (bool): Flag indicating whether it's the P-wave onset (True) or offset (False).\n",
        "\n",
        "    Returns:\n",
        "    - p_onset_offset (int): Index of the P-wave onset or offset in the signal.\n",
        "    \"\"\"\n",
        "    window = signal[p_peak - 100:p_peak + 100]  # Create a window around the P-wave peak\n",
        "    z = find_peaks(window)  # Find peaks in the window\n",
        "    peaks = []\n",
        "    peaksValue = []\n",
        "\n",
        "    # Extract peak indices and values from the window\n",
        "    for k in range(len(z[0])):\n",
        "        index = np.where(signal == window[z[0][k]])[0][0]  # Get the index of the peak in the original signal\n",
        "        peaks.append(index)\n",
        "        peaksValue.append(signal[index])\n",
        "\n",
        "    peaksValue.sort()  # Sort peak values in ascending order\n",
        "    threshold = 0.002  # Threshold for distinguishing peaks\n",
        "\n",
        "    p_onset_offset = 0  # Initialize P-wave onset or offset index\n",
        "\n",
        "    if len(z[0]) == 0:\n",
        "        # If no peaks are detected in the window, estimate onset or offset using minimum radius of curvature\n",
        "        if isOnset:\n",
        "            p_onset_offset = minimumRadiusOfCurvature(signal, p_peak, 100, True)\n",
        "        else:\n",
        "            p_onset_offset = minimumRadiusOfCurvature(signal, p_peak, qrs_onset - p_peak, False)\n",
        "    else:\n",
        "        p_peak1 = np.where(signal == peaksValue[-1])  # Index of the highest peak in the window\n",
        "\n",
        "        # Check if the difference between the highest and ,second-highest peaks is below the threshold\n",
        "\n",
        "        if round(peaksValue[-1] - peaksValue[-2], 3) <= threshold:\n",
        "            p_peak2 = np.where(signal == peaksValue[-2])  # Index of the second-highest peak in the window\n",
        "\n",
        "            if isOnset:\n",
        "                p_onset_offset = minimumRadiusOfCurvature(signal, p_peak1, 100, True)\n",
        "            else:\n",
        "                if p_peak2 < qrs_onset:\n",
        "                    p_onset_offset = minimumRadiusOfCurvature(signal, p_peak2, qrs_onset - p_peak2, False)\n",
        "                else:\n",
        "                    p_onset_offset = minimumRadiusOfCurvature(signal, p_peak2, qrs_onset, False)\n",
        "        else:\n",
        "            # If peaks are distinct, estimate onset or offset using minimum radius of curvature\n",
        "            if isOnset:\n",
        "                p_onset_offset = minimumRadiusOfCurvature(signal, p_peak, 100, True)\n",
        "            else:\n",
        "                p_onset_offset = minimumRadiusOfCurvature(signal, p_peak, qrs_onset - p_peak, False)\n",
        "\n",
        "    return p_onset_offset  # Return the P-wave onset or offset index\n",
        "\n",
        "\n",
        "def findT(signal, qrs_offset):\n",
        "    \"\"\"\n",
        "    Finds the T-wave index in a signal given the QRS offset index.\n",
        "\n",
        "    Parameters:\n",
        "    - signal (numpy array): Input signal.\n",
        "    - qrs_offset (int): Index of the QRS offset in the signal.\n",
        "\n",
        "    Returns:\n",
        "    - T_peak (int): Index of the T-wave peak in the signal.\n",
        "    \"\"\"\n",
        "    j = qrs_offset + 100  # Start searching 100 samples after QRS offset\n",
        "    if j > len(signal) - 1:\n",
        "        j = len(signal) - 1  # Ensure j is within signal range\n",
        "\n",
        "    max_val = -np.inf  # Initialize maximum value as negative infinity\n",
        "    index = 0  # Initialize index variable\n",
        "\n",
        "    # Search for the T-wave peak index\n",
        "    while j < len(signal):\n",
        "        if signal[j] > max_val:\n",
        "            max_val = signal[j]\n",
        "            index = j\n",
        "        j += 1\n",
        "\n",
        "    # Check if the T-wave peak is downward\n",
        "    if signal[index] == 0:\n",
        "        j = qrs_offset + 400  # Start searching 400 samples after QRS offset for downward T-wave\n",
        "        min_val = np.inf  # Initialize minimum value as positive infinity\n",
        "\n",
        "        # Search for the downward T-wave peak index\n",
        "        while j < len(signal):\n",
        "            if signal[j] < min_val:\n",
        "                min_val = signal[j]\n",
        "                index = j\n",
        "            j += 1\n",
        "\n",
        "    T_peak = index  # T-wave peak index is the index of the maximum value after QRS offset\n",
        "    return T_peak  # Return the T-wave peak index\n",
        "\n",
        "\n",
        "def findFiducialPoints(segment_):\n",
        "    \"\"\"\n",
        "    Finds multiple fiducial points in an ECG signal.\n",
        "\n",
        "    Parameters:\n",
        "    - signal (numpy array): Input ECG signal.\n",
        "\n",
        "    Returns:\n",
        "    - fiducialPoints (list): List of fiducial points including R, Q, S peaks,\n",
        "                             QRS onset and offset, P peak, P onset and offset,\n",
        "                             T peak, T onset and offset.\n",
        "    \"\"\"\n",
        "    try:\n",
        "        fiducialPoints = []  # Initialize list to store fiducial points\n",
        "\n",
        "        # Find R peak and append to fiducial points list\n",
        "        R = findR(segment_)\n",
        "        fiducialPoints.append(R)\n",
        "\n",
        "        # Find Q peak and append to fiducial points list\n",
        "        Q = findQ(segment_, R)\n",
        "        fiducialPoints.append(Q)\n",
        "\n",
        "        # Find S peak and append to fiducial points list\n",
        "        S = findS(segment_, R)\n",
        "        fiducialPoints.append(S)\n",
        "\n",
        "        # Find QRS onset and append to fiducial points list\n",
        "        qrs_onset = findqrsOnset(segment_, R)\n",
        "        fiducialPoints.append(qrs_onset)\n",
        "\n",
        "        # Find QRS offset and append to fiducial points list\n",
        "        qrs_offset = findqrsOffset(segment_, R)\n",
        "        fiducialPoints.append(qrs_offset)\n",
        "\n",
        "        # Find P peak and append to fiducial points list\n",
        "        p_peak = findP(segment_, qrs_onset)\n",
        "        fiducialPoints.append(p_peak)\n",
        "\n",
        "        # Find P onset and append to fiducial points list\n",
        "        p_onset = findPon(segment_, p_peak, qrs_onset, True)\n",
        "        fiducialPoints.append(p_onset)\n",
        "\n",
        "        # Find P offset and append to fiducial points list\n",
        "        p_offset = findPon(segment_, p_peak, qrs_onset, False)\n",
        "        fiducialPoints.append(p_offset)\n",
        "\n",
        "        # Find T peak and append to fiducial points list\n",
        "        T_peak = findT(segment_, qrs_offset)\n",
        "        fiducialPoints.append(T_peak)\n",
        "\n",
        "        # Find T onset and append to fiducial points list\n",
        "        t_onset = minimumRadiusOfCurvature(segment_, T_peak, 200, True)\n",
        "        fiducialPoints.append(t_onset)\n",
        "\n",
        "        # Find T offset and append to fiducial points list\n",
        "        t_offset = minimumRadiusOfCurvature(segment_, T_peak, 300, False)\n",
        "        fiducialPoints.append(t_offset)\n",
        "        return fiducialPoints  # Return the list of fiducial points\n",
        "\n",
        "    except Exception as e:\n",
        "        return None\n",
        "\n",
        "\n",
        "def findFeatures(fiducialPoints, signal):\n",
        "    \"\"\"\n",
        "    Calculates features based on fiducial points and the signal.\n",
        "\n",
        "    Parameters:\n",
        "    - fiducialPoints (list): List of fiducial points in the ECG signal.\n",
        "    - signal (numpy array): Input ECG signal.\n",
        "\n",
        "    Returns:\n",
        "    - features (list): List of calculated features.\n",
        "    \"\"\"\n",
        "    features = [fiducialPoints[0] - fiducialPoints[1], fiducialPoints[2] - fiducialPoints[0],\n",
        "                fiducialPoints[7] - fiducialPoints[6], fiducialPoints[10] - fiducialPoints[9],\n",
        "                fiducialPoints[5] - fiducialPoints[1], fiducialPoints[1] - fiducialPoints[2],\n",
        "                fiducialPoints[4] - fiducialPoints[8], fiducialPoints[6] - fiducialPoints[0],\n",
        "                fiducialPoints[0] - fiducialPoints[9], fiducialPoints[5] - fiducialPoints[0],\n",
        "                fiducialPoints[0] - fiducialPoints[8], fiducialPoints[5] - fiducialPoints[2],\n",
        "                fiducialPoints[1] - fiducialPoints[8], fiducialPoints[6] - fiducialPoints[0],\n",
        "                fiducialPoints[0] - fiducialPoints[10], fiducialPoints[6] - fiducialPoints[1],\n",
        "                fiducialPoints[2] - fiducialPoints[10], fiducialPoints[7] - fiducialPoints[3],\n",
        "                fiducialPoints[4] - fiducialPoints[9], fiducialPoints[7] - fiducialPoints[10],\n",
        "                fiducialPoints[5] - fiducialPoints[8], signal[fiducialPoints[0]] - signal[fiducialPoints[1]],\n",
        "                signal[fiducialPoints[8]] - signal[fiducialPoints[5]],\n",
        "                signal[fiducialPoints[5]] - signal[fiducialPoints[2]],\n",
        "                signal[fiducialPoints[8]] - signal[fiducialPoints[1]],\n",
        "                signal[fiducialPoints[0]] - signal[fiducialPoints[5]],\n",
        "                signal[fiducialPoints[0]] - signal[fiducialPoints[8]],\n",
        "                signal[fiducialPoints[1]] - signal[fiducialPoints[2]],\n",
        "                signal[fiducialPoints[8]] - signal[fiducialPoints[1]],\n",
        "                signal[fiducialPoints[8]] - signal[fiducialPoints[2]],\n",
        "                signal[fiducialPoints[0]] - signal[fiducialPoints[2]]]  # Initialize list to store features\n",
        "\n",
        "    # Calculate features based on fiducial points and signal\n",
        "\n",
        "    return features  # Return the list of calculated features\n"
      ],
      "metadata": {
        "id": "o3EOnmqPKzVx"
      },
      "execution_count": 138,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "def feature_extraction(segments, segments_labels):\n",
        "    Labels = []\n",
        "    Features = []\n",
        "    for c in range(segments.shape[0]):\n",
        "        g = np.array(segments[c, :])\n",
        "        points = findFiducialPoints(g)\n",
        "        if points is not None:\n",
        "            features = findFeatures(points, g)\n",
        "            Features.append(features)\n",
        "            Labels.append(segments_labels[c])\n",
        "    Features = np.array(Features)\n",
        "    Labels = np.array(Labels)\n",
        "    return Features, Labels\n",
        "\n",
        "\n"
      ],
      "metadata": {
        "id": "4xIm40gbQt3n"
      },
      "execution_count": 139,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "# Train Models"
      ],
      "metadata": {
        "id": "xqNle-iVSVE9"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "X_train_seg, X_test_seg, y_train_seg, y_test_seg = train_test_split(X, y, test_size=0.2, random_state=42,stratify=y)\n",
        "\n",
        "\n",
        "X_train, y_train = feature_extraction(X_train_seg, y_train_seg)\n",
        "print(X_train.shape)\n",
        "print(y_train.shape)\n"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "ujehWrkARv6L",
        "outputId": "6e1d5318-8d75-4d45-fd74-8a884f2597bc"
      },
      "execution_count": 154,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "(606, 31)\n",
            "(606,)\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "# Create an SVM classifier\n",
        "svm_classifier = SVC(kernel='rbf', C=1.0, random_state=42, probability=True)\n",
        "\n",
        "# Train the classifier on the training data\n",
        "svm_classifier.fit(X_train, y_train)\n",
        "\n",
        "X_test, y_test = feature_extraction(X_test_seg, y_test_seg)\n",
        "\n",
        "# Make predictions on the testing data\n",
        "y_pred = svm_classifier.predict(X_test)\n",
        "\n",
        "y_pred_prob = svm_classifier.predict_proba(X_test)\n",
        "\n",
        "for i in range(y_pred_prob.shape[0]):\n",
        "   if not y_pred_prob[i].any() > 0.9:\n",
        "      y_pred[i] = 'unknown'\n",
        "\n",
        "\n",
        "# Evaluate the classifier\n",
        "accuracy = accuracy_score(y_test, y_pred)\n",
        "print(f\"Accuracy: {accuracy}\")\n"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "YHTd5T-KScJ8",
        "outputId": "e79d6cec-24c6-4f2b-c057-f042ca1c8aa2"
      },
      "execution_count": 161,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Accuracy: 0.9864864864864865\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "from sklearn.tree import DecisionTreeClassifier\n",
        "# Create a Decision Tree classifier\n",
        "dt_classifier = DecisionTreeClassifier(random_state=42)\n",
        "\n",
        "# Train the classifier on the training data\n",
        "dt_classifier.fit(X_train, y_train)\n",
        "\n",
        "# Make predictions on the testing data\n",
        "y_pred = dt_classifier.predict(X_test)\n",
        "\n",
        "# Evaluate the classifier\n",
        "Rate = accuracy_score(y_test, y_pred)\n",
        "print(f\"Accuracy: {Rate}\")\n"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "Qeq99nzfWK3H",
        "outputId": "836224e8-5dbc-40c7-d98e-b6434099fb8d"
      },
      "execution_count": 162,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Accuracy: 0.9932432432432432\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "from sklearn.neighbors import KNeighborsClassifier\n",
        "knn_classifier = KNeighborsClassifier(n_neighbors=4)\n",
        "\n",
        "# Train the classifier on the training data\n",
        "knn_classifier.fit(X_train, y_train)\n",
        "\n",
        "# Make predictions on the testing data\n",
        "y_pred = knn_classifier.predict(X_test)\n",
        "\n",
        "# Evaluate the classifier\n",
        "accuracy = accuracy_score(y_test, y_pred)\n",
        "print(f\"Accuracy: {accuracy}\")\n"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "oPLTiVttXN0O",
        "outputId": "57007803-3d5f-4f9c-c24c-9b11042ab7d2"
      },
      "execution_count": 163,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Accuracy: 1.0\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# dump all models trained to be used"
      ],
      "metadata": {
        "id": "YfMTyzGpcL-Y"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "with open('/content/drive/MyDrive/ECG_Project/Models/SVM.pkl', 'wb') as f:\n",
        "    pickle.dump(svm_classifier, f)\n",
        "with open('/content/drive/MyDrive/ECG_Project/Models/knn_classifier.pkl', 'wb') as f:\n",
        "    pickle.dump(knn_classifier, f)\n",
        "with open('/content/drive/MyDrive/ECG_Project/Models/dt_classifier.pkl', 'wb') as f:\n",
        "    pickle.dump(dt_classifier, f)\n",
        "\n",
        "\n",
        "\n"
      ],
      "metadata": {
        "id": "LOVHtfw2caFo"
      },
      "execution_count": 164,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "with open('/content/drive/MyDrive/ECG_Project/Models/X_test.pkl', 'wb') as f:\n",
        "    pickle.dump(X_test_seg, f)\n",
        "with open('/content/drive/MyDrive/ECG_Project/Models/y_test.pkl', 'wb') as f:\n",
        "    pickle.dump(y_test_seg, f)\n"
      ],
      "metadata": {
        "id": "ROjJyvQzlDOx"
      },
      "execution_count": 165,
      "outputs": []
    }
  ]
}